
package com.example.springcloudeurekaserverproducer.mybatis.interceptor;


import com.example.springcloudeurekaserverproducer.mybatis.Constants;
import com.example.springcloudeurekaserverproducer.mybatis.PageVo;
import com.example.springcloudeurekaserverproducer.mybatis.interceptor.dialect.MySqlDialect;
import com.example.springcloudeurekaserverproducer.mybatis.interceptor.dialect.OracleDialect;
import org.apache.commons.lang.StringUtils;
import org.apache.ibatis.builder.SqlSourceBuilder;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.DefaultReflectorFactory;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.ReflectorFactory;
import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
import org.apache.ibatis.reflection.factory.ObjectFactory;
import org.apache.ibatis.reflection.wrapper.DefaultObjectWrapperFactory;
import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.RowBounds;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.util.List;
import java.util.Map;
import java.util.Properties;


/**
 * 
 * @ClassName: AutoMapperInterceptor
 * @Description: 对于实现自己的Interceptor而言有两个很重要的注解，
 *               一个是@Intercepts，其值是一个@Signature数组。
 *               @Intercepts用于表明当前的对象是一个Interceptor，
 *               而@Signature则表明要拦截的接口、方法以及对应的参数类型
 * @author lanyuansong
 * @date 2018年7月11日
 */
@Intercepts({ @Signature(type = StatementHandler.class, method = "prepare", args = { Connection.class,Integer.class }) })
@Component
public class AutoMapperInterceptor implements Interceptor {
//	 @Value("${datasource.driverClassName}")
//	 String dirverType;
	private Logger logger = LoggerFactory
			.getLogger(AutoMapperInterceptor.class);
    
	protected static final ObjectFactory DEFAULT_OBJECT_FACTORY = new DefaultObjectFactory();

	protected static final ObjectWrapperFactory OBJECT_WRAPPER_FACTORY = new DefaultObjectWrapperFactory();

	protected static final ReflectorFactory REFLECTOR_FACTORY = new DefaultReflectorFactory();
	
	protected Properties properties = new Properties();

	private String dirverType;
	private int transactionTimeout;
	
	@Override
	public Object intercept(Invocation invocation) throws Throwable {
		StatementHandler statementhandler = (StatementHandler) invocation.getTarget();
//        BaseStatementHandler delegate = (BaseStatementHandler) ReflectHelper.getValueByFieldName(statementhandler, "delegate");
//        MappedStatement mappedStatement = (MappedStatement) ReflectHelper.getValueByFieldName(delegate, "mappedStatement");
//	    long startTime = System.currentTimeMillis();
//        try {
//            return invocation.proceed();
//        } finally {
//            long endTime = System.currentTimeMillis();
//            long sqlCost = endTime - startTime;
//            log.info("执行耗时 : [" + sqlCost + "ms ] ");
//        }
        long start,end;
        start = System.currentTimeMillis();
		MetaObject metaobject = MetaObject.forObject(statementhandler, DEFAULT_OBJECT_FACTORY,
				OBJECT_WRAPPER_FACTORY, REFLECTOR_FACTORY);
		
		while (metaobject.hasGetter("h")) {  
            Object object = metaobject.getValue("h");  
            metaobject = MetaObject.forObject(object, DEFAULT_OBJECT_FACTORY, OBJECT_WRAPPER_FACTORY, REFLECTOR_FACTORY);
        }  
        // 分离最后一个代理对象的目标类  
        while (metaobject.hasGetter("target")) {  
            Object object = metaobject.getValue("target");  
            metaobject = MetaObject.forObject(object, DEFAULT_OBJECT_FACTORY, OBJECT_WRAPPER_FACTORY, REFLECTOR_FACTORY);
        }

		
//		ParameterHandler	parameterHandler=statementhandler.getParameterHandler();//获取参数
//		Object obj=	parameterHandler.getParameterObject();
		MappedStatement mappedStatement = (MappedStatement) metaobject.getValue("delegate.mappedStatement");
		Configuration configuration = (Configuration) metaobject.getValue("delegate.configuration");
		String classAndMethod = mappedStatement.getId();
		// 获取方法名称，如果方法名称相同了，那么参数类型肯定不同,在mybatis的xml里面select，update，delete，insert
		// 标签的id必须和方法名称一一对应，包括参数类型也必须一一对应
		String methodName = classAndMethod.substring(classAndMethod.lastIndexOf(".") + 1);
//		logger.info(methodName);
		BoundSql boundSql = statementhandler.getBoundSql();
          // 该sql有可能没有
		String sql = boundSql.getSql();
		
		String newsql = null;
		Object parameterObject = boundSql.getParameterObject();//获取参数非代理
		// 如果没有sql语句的话
		if (StringUtils.isBlank(sql)) {
			// 对sql进行拼装
			if (methodName.startsWith("save") || methodName.startsWith("add") || methodName.startsWith("insert")) {
				newsql = HumpBuilder.builderInsert(parameterObject);
			} else if (methodName.startsWith("update") || methodName.startsWith("edit")) {
				newsql = HumpBuilder.builderUpdate(parameterObject);
			} else if (methodName.startsWith("delete") || methodName.startsWith("remove")) {
				newsql = HumpBuilder.builderDelete(parameterObject);
			}
			 else if (methodName.startsWith("get") || methodName.startsWith("select")) {
					newsql = HumpBuilder.buildSelectSql(parameterObject);
				}
			 else if (methodName.startsWith("queryById") ) {
					newsql = HumpBuilder.buildSelectSqlById(parameterObject);
				}
			SqlSource sqlSource = buildSqlSource(configuration, newsql,parameterObject.getClass());
			List<ParameterMapping> parameterMappings = sqlSource.getBoundSql(parameterObject).getParameterMappings();
			metaobject.setValue("delegate.boundSql.sql",sqlSource.getBoundSql(parameterObject).getSql());
			metaobject.setValue("delegate.boundSql.parameterMappings", parameterMappings);
		}else{
			if(methodName.startsWith("page")){
//				String dirverType = properties.getProperty("driver");
				if(parameterObject instanceof PageVo){
					PageVo<?> pageVo = (PageVo<?>)parameterObject;
					int pageSize = pageVo.getPageSize();
					int pageNow = pageVo.getPageNow();
					String paginationSql = null;
					if(dirverType.equals(Constants.ORACLE_DRIVER)){
						 paginationSql = new OracleDialect().buildPaginationSql(sql,pageNow, pageSize);
					}else if(dirverType.equals(Constants.MYSQL_DRIVER)){
						 paginationSql = new MySqlDialect().buildPaginationSql(sql,pageNow, pageSize);
					}else{
						throw new RuntimeException("不支持该类型的sql");
					}
					
					//String paginationSql = new OracleDialect().buildPaginationSql(sql, pageSize, pageNow);
					List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
					 // 禁用内存分页
					SqlSource sqlSource = buildSqlSource(configuration, paginationSql,parameterObject.getClass());
					metaobject.setValue("delegate.boundSql.sql",sqlSource.getBoundSql(parameterObject).getSql());
					metaobject.setValue("delegate.boundSql.parameterMappings", parameterMappings);
					metaobject.setValue("delegate.rowBounds.offset", RowBounds.NO_ROW_OFFSET);
					metaobject.setValue("delegate.rowBounds.limit", RowBounds.NO_ROW_LIMIT);
				}
			}
		}
		// 调用原始statementHandler的prepare方法 返回主权
		statementhandler = (StatementHandler) metaobject.getOriginalObject();
     	statementhandler.prepare((Connection) invocation.getArgs()[0], transactionTimeout);//新特性
        Object retValue = null;
        // 传递给下一个拦截器处理
        //打印sql执行时间

        retValue = invocation.proceed();
        
        end = System.currentTimeMillis();  
//        System.out.println("start time:" + start+ "; end time:" + end+ "; Run Time:" + (end - start) + "(ms)");
//        long total = end - start;
//	    	String sql1 = (String)metaobject.getValue("delegate.boundSql.sql");
//	    	logger.info("该sql:"+sql1+"花费时间:"+total);
        return retValue;
		//return statementhandler;
	}

	@Override
	public Object plugin(Object target) {
		if (target instanceof StatementHandler) {
			return Plugin.wrap(target, this);
		} else {
			return target;
		} 
	}

	@Override
	public void setProperties(Properties properties) {
		//加载第三方Properties 配置文件
//		load();
		//String driver = properties.getProperty("driver");
//		String dialect = properties.getProperty("dialect");
		this.dirverType= properties.getProperty("dirverType");
		this.transactionTimeout= Integer.parseInt(properties.getProperty("transactionTimeout"));
	}



	private void load(){
		InputStream inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("jfconnectconfig.properties");
		try {
			properties.load(inputStream);
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			IOUtils.closeQuietly(inputStream);
		}
	}
    /**
     * 
    * @Title: buildSqlSource
    * @author lanyuansong
    * @Description: TODO(这里用一句话描述这个方法的作用)
    * @param @param configuration
    * @param @param originalSql
    * @param @param parameterType
    * @param @return    参数
    * @return SqlSource    返回类型
    * @throws
     */
	protected SqlSource buildSqlSource(Configuration configuration,
			String originalSql, Class<?> parameterType) {
		SqlSourceBuilder builder = new SqlSourceBuilder(configuration);
		return builder.parse(originalSql, parameterType, null);
	}
	
	private SqlSource buildSqlSource(Configuration configuration,String originalSql, Class<?> parameterType,Map<String, Object> additionalParameters) {
		SqlSourceBuilder builder = new SqlSourceBuilder(configuration);
		return builder.parse(originalSql, parameterType, additionalParameters);
	}
	
	
	public static void main(String[] args) {
		 Properties properties = new Properties();
		String dirverType = properties.getProperty("driver");
	}
	

}
